// GerikSpellsPythonIFace.h
// A spell interface for Civ4 written by Gerikes

#ifndef GerikSpellsPythonIFace_H
#define GerikSpellsPythonIFace_H

class CvSelectionGroup;
struct CvWidgetDataStruct;

// This is a singleton class
class GerikSpellsPythonIFace
{
public:
	// singleton accessor
	static GerikSpellsPythonIFace& GetInstance();

	GerikSpellsPythonIFace();
	virtual ~GerikSpellsPythonIFace();

	// **************************************************************************
	// Functions used while the action button is visible, but not pressed yet.
	// **************************************************************************

	// canGroupCast 
	// Determines if the action button will appear for a group of selected units. It is this
	// function that is called by CvGame::canHandleAction to determine whether or not to place
	// the action button.
	bool canGroupCast(SpellTypes eSpell, CvSelectionGroup* pGroup, CvPlot* pPlot, bool bVisible);

	// parseSpellHelp
	// Adds to the Action help string the string for this spell.
	void setSpellHelpText(CvWString &szBuffer, SpellTypes eSpell, CvSelectionGroup* pSelectedGroup, CvPlot* pPlot);
	
	// *******************************************************************************
	// Functions that direct what happens after a  spell's action button is clicked,
	// and do other things while a spell is still activated.
	// *******************************************************************************
	
	// handleSpellAction
	// Handles what happens when the action button for a spell is clicked
	// Typically, if the action is an immediate action, it just calls the python function.
	// If the spell requires a plot target or pie menu, this does the necessary calls to enter into
	// the correct interface mode.
	void handleSpellActionClick(SpellTypes eSpell, CvSelectionGroup* pSelectedGroup, CvPlot* pPlot, bool bAlt, bool bShift, bool bCtrl);

	// parseTargetInfo
	// Calls the python spell file to give a string for a target button that the user is hovering
	// over.
	void parseTargetInfo(CvWidgetDataStruct& widgetDataStruct, CvWString& szBuffer);

	// *******************************************************************************
	// Function used to call the python functions to perform the actual actions of a
	// a spell.
	// *******************************************************************************

	// The actual spell is "encoded" into iFlags, since we didn't have room for it
	// to be kept within the MissionData structure. It will be deencoded before
	// going to python.

	// Tells all computers to have the selection group selected on the calling
	// computer to push a mission, which is a spell with the proper data.
	// This will be called from the python files.
	void pushSpellMission(SpellTypes eSpell, int iData1, int iData2, int iFlags);
	void pushSpellMission(SpellTypes eSpell, int iData1, int iData2, int iFlags, bool bAlt, bool bShift);
	
	// Calls the python function that tell the spell to go through any group startup
	// activities. This is called once, as soon as the selection group goes through the
	// "startMission" function.
	// Returns true if the spell should be continued.
	bool startSpellResults(CvSelectionGroup* pSelectedGroup, int iData1, int iData2, int iData3);
	
	// Calls the python functions to actually DO what a spell does (deal damage,
	// change terrain, etc). This function returns true if the mission should
	// be immediately discarded after the spell results python function is called.
	// If the spell continues for a longer amount of time, than this returns false.
	// This is called whenever "continueMission" is called for a group.
	bool continueSpellResults(CvSelectionGroup* pSelectedGroup, int iData1, int iData2, int iData3);
	
	// Calls the python function to allow spells to cleanup once a group if a group removes the spell from
	// it's queue.
	void cleanupSpell(CvSelectionGroup* pCastingGroup, int iData1, int iData2, int iData3);

	// *******************************************************************************
	// Functions used to for spells that go on for longer than a turn.
	// *******************************************************************************
	
	// Gets a string that will be put into the unit's help pane.
	void setSpellUnitHelpText(CvWString &szBuffer, const CvUnit* pUnit);

};

// Some utilities. Mainly for taking the eSpell and iData1 variables and comining them into
// one variable. This is needed because the spell needs three data values (spell, iData1, iData2)
// and there are only enough for two values.
namespace GerikSpellsUtils
{
	const static TCHAR* GERIK_SPELL_MODULE = "GerikSpellsInterface";
	
	const int ENCODED_BITS_RESERVED_DATA = 22;
	const int ENCODED_BITS_RESERVED_SPELL = 10;
	const int MAX_NUM_SPELLS = (int)(pow(2, ENCODED_BITS_RESERVED_SPELL));
	
	// *******************************************************************************
	// Functions that can encode/deencode an integer variable to store two integer
	// variables. Used to put both the eSpell and the iData3 both in the iFlags.
	// *******************************************************************************
	inline int encodeFlagVariable(SpellTypes eSpell, int iFlags)
	{
		FAssertMsg(eSpell >= 0, "eSpell is negative while encoding data variable iData1! Data will be corrupted!");
		return (iFlags << ENCODED_BITS_RESERVED_SPELL) + eSpell;
	}

	inline void deencodeFlagVariable(int iEncodedFlags, SpellTypes &eSpell, int &iFlags)
	{
		iFlags = iEncodedFlags >> ENCODED_BITS_RESERVED_SPELL;
		eSpell = (SpellTypes)(iEncodedFlags - (iFlags << ENCODED_BITS_RESERVED_SPELL));
	}

	inline void deencodeUtfToWideString(CvString& szUtf, CvWString& szwResults)
	{
		const char* szResultChars = szUtf.GetCString();

		byte iValue = 0;
		int iCurrentByte = 0;
		int iSequence = 0;
		int iRemainingLengthOfSequence = 0;

		while (szResultChars[iCurrentByte] != 0)
		{
			iValue = (byte)szResultChars[iCurrentByte];
			if (iValue < 128)
			{
				// We recieved a basic, ascii string (byte started with 0)

				// Check for an error (we were expecting a utf sequence
				if (iRemainingLengthOfSequence > 0)
				{
					FAssertMsg(false, "GerikSpells: Expecting UTF sequence continuation, recieved ascii value");
					return;
				}

				// Append the ascii's ordinal value to the string.
				szwResults += iValue;
			}
			// Start of a new sequence.
			else if (iValue >= 192)
			{
				// We recieved a byte starting with 1*0, where * is one or more 1's.
				// The number of 1's in the * determines how many bytes will house this sequence of bits.

				// Check if we're impeding on a utf sequence already in progress
				if (iRemainingLengthOfSequence > 0)
				{
					FAssertMsg(false, "GerikSpells: Expecting UTF sequence continuation, recieved UTF sequence start.");
					return;
				}
				
				// Determine how long the sequence will be.
				// Continuously shift the bits until we find the first non-zero bit (after the first, obviously).
				byte iTempValue = iValue;
				while (iTempValue >= 128)
				{
					iRemainingLengthOfSequence += 1;
					iTempValue <<= 1;
				}

				// We now have a zero as the MSB, with the shifted "first few bits" next to it.
				// Shift it back to find the first few bits of the utf character.
				iTempValue >>= iRemainingLengthOfSequence;
				iSequence += iTempValue;

				// This one counts as part of the sequence, so decrement.
				--iRemainingLengthOfSequence;
			}
			else
			{
				// We recieved a byte starting with "10", a continuation of a sequence
				
				// Check if we're supposed to be in a sequence
				if (iRemainingLengthOfSequence == 0)
				{
					FAssertMsg(false, "GerikSpells: Expecting start of new sequence or ascii value, recieved continuation of sequence.");
					return;
				}

				iSequence <<= 6; // Shift over 6 bits, allowing room for this part of the sequence.
				iSequence += (iValue % 128);
				--iRemainingLengthOfSequence;

				// If this was the last byte in the sequence, we've completed the sequence.
				// Add this as the next value of the character
				if (iRemainingLengthOfSequence == 0)
				{
					szwResults += iSequence;
					iSequence = 0;
				}
				
				// Clear the sequence.
				
			}

			// Can't forget the increment!
			++iCurrentByte;
		}
	}

};

#endif /* GerikSpellsPythonIFace_H */
